package cn.microven.mindmap.struture;

import cn.microven.mindmap.components.node.AbstractMapNode;
import cn.microven.mindmap.components.node.RectangleNode;
import cn.microven.mindmap.components.part.BothSideMapPart;
import cn.microven.mindmap.components.part.Part;
import cn.microven.mindmap.controller.menu.FontStyleMenu;
import cn.microven.mindmap.oprations.ChangeLayoutOperation;
import cn.microven.mindmap.oprations.Operation;
import cn.microven.mindmap.utils.ControllerGetter;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Paint;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;

/**
 * 思维导图类
 * 管理图的 Undo Redo 保存 等功能
 * 使用单例模式
 *
 * @author 吴博维
 * @version 1.0
 */
public final class MindMap extends AnchorPane {

    public static boolean isSelected = true;
    /**
     * 根结点
     */
    private BothSideMapPart root = new BothSideMapPart(new RectangleNode());

    /**
     *选中的节点
     */
    private SimpleObjectProperty<AbstractMapNode> selectedNode = new SimpleObjectProperty<>();

    /**
     * 实例（单例模式）
     */
    private static MindMap map;

    /**
     * 默认节点
     */
    private RectangleNode defaultNode = new RectangleNode();


    /**
     * 操作列表：
     * 下标指向上一个操作
     * 进行撤销时调用Operation的undo()后下标前移
     * 取消撤销时下标后移，调用Operation的start()方法
     *
     * 添加新的操作时删除当前下标之后的所有元素
     */
    private ArrayList<Operation> operations = new ArrayList<>();

    private int oprIndex = -1;

    private String layout = "BothSide";

    private boolean isRedoing = false;

    private boolean isUndoing = false;

    private Instant selectedNodeChangeTime = Instant.now();


    /**
     * 构造方法 私有化
     */
    private MindMap() {
        this.root.heightProperty().addListener((number)->{
            this.setPrefHeight(root.getHeight());
        });

        this.getChildren().add((Node) root);

        this.selectedNodeProperty().addListener(((observableValue, abstractMapNode, t1) -> {
            if (abstractMapNode != null)
                abstractMapNode.deleteSelectedStyle();
            if (t1 != null)
                t1.appendSelectedStyle();
        }));

        this.setOnMouseClicked((mouseEvent -> {
            if (Duration.between(this.getSelectedNodeChangeTime(),Instant.now() ).toMillis() > 300) {
                MindMap.getInstance().clearSelect();
            }
        }));

        this.selectedNode.addListener(((observableValue, node, t1) -> {
            selectedNodeChangeTime = Instant.now();
        }));

        this.root.layoutYProperty().addListener(((observableValue, number, t1) -> {
            this.setHeight(this.root.getHeight()+Math.abs(this.root.getLayoutY()));
        }));

        this.root.relocate(100,100);

        this.drag();
    }

    private static class Position {
        double x;
        double y;
    }


    public static MindMap getInstance() {
        return map;
    }


    public static MindMap initInstance() throws Exception {
        //已存在实例则抛出异常
        if (map != null) {
            throw new Exception("MindMap instance already exists");
        }

        //初始化实例
        map = new MindMap();
        return map;
    }


    public SimpleObjectProperty<AbstractMapNode> selectedNodeProperty() {
        return selectedNode;
    }

    /**
     * 添加操作
     * @param opr 已进行的操作
     */
    public static void addOperation(Operation opr) {


        //添加操作 指针后移
        MindMap map = MindMap.getInstance();
        map.operations.add(opr);
        map.oprIndex ++;

        //Undo后添加操作删除指针后的操作（已使用Undo丢弃）
        if (map.oprIndex != map.operations.size()) {
            for (int i = map.oprIndex+1;i< map.operations.size();i++) {
                map.operations.remove((int)i);
            }
        }

    }

    public static Operation getLastOperation() {
        MindMap map = MindMap.getInstance();
        if (map.getOperations().size() == 0) return null;
        if (map.getOprIndex() == -1) return null;
        return map.getOperations().get(map.getOprIndex());
    }

    /**
     * 撤销操作
     */
    public static void redo() {
        if (map.oprIndex < map.operations.size()-1) {
            map.oprIndex ++;
            map.isRedoing = true;
            map.operations.get(map.oprIndex).start(true);
            map.updateMenu();
            map.isRedoing = false;
        }
    }

    public void drag() {
        final Position pos = new Position();

        this.root.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
            this.root.setCursor(Cursor.MOVE);

            pos.x = event.getX();
            pos.y = event.getY();
        });
        this.root.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> this.root.setCursor(Cursor.DEFAULT));

        this.root.addEventHandler(MouseEvent.MOUSE_DRAGGED, event -> {
            double distanceX = event.getX() - pos.x;
            double distanceY = event.getY() - pos.y;

            double x = this.root.getLayoutX() + distanceX;
            double y = this.root.getLayoutY() + distanceY;

            // 计算出 x、y 后将结点重定位到指定坐标点 (x, y)
            this.root.relocate(x, y);
        });
    }

    /**
     * 取消撤销
     */
    public static void undo() {
        if (map.oprIndex >= 0 && map.operations.size() > 0) {
            map.operations.get(map.oprIndex).undo();
            map.oprIndex --;
            map.isUndoing = true;
            map.updateMenu();
            map.isUndoing = false;
        }
    }

    public void castLayout(String layout) {
        ChangeLayoutOperation clo = new ChangeLayoutOperation(this.getLayout(),layout);
        clo.start();
        this.operations.clear();
        this.oprIndex = -1;
        this.layout = layout;
    }

    public void select(AbstractMapNode node) {
        this.selectedNode.setValue(node);
        if (selectedNode.getValue() != null)
        {
            this.isRedoing = true;
            updateMenu();
            this.isRedoing = false;
        }
    }

    public void clearSelect() {
        select(defaultNode);
    }

    public AbstractMapNode getSelectedNode() {
        if (this.selectedNode.getValue() == null) {
            return this.defaultNode;
        }
        return this.selectedNode.getValue();
    }

    public void updateMenu() {
        ControllerGetter.getFontStyleMenu().update();
        ControllerGetter.getNodeStyleMenu().update();
    }

    /**
     * Getters
     */
    public Part getRoot() {
        return root;
    }

    public static MindMap getMap() {
        return map;
    }

    public String getLayout() {
        return map.layout;
    }

    public ArrayList<Operation> getOperations() {
        return map.operations;
    }

    public int getOprIndex() {
        return map.oprIndex;
    }

    public boolean isRedoing() {
        return isRedoing;
    }

    public boolean isUndoing() {
        return isUndoing;
    }

    public Instant getSelectedNodeChangeTime() {
        return selectedNodeChangeTime;
    }

    public RectangleNode getDefaultNode() {
        return defaultNode;
    }

    /**
     * Setter
     */
    public void setLayout(String layout) {
        map.layout = layout;
    }

    public void setMap(MindMap map) {
        MindMap.map = map;
    }

    public void setRoot(BothSideMapPart root) {
        if (this.root!=null) {
            this.getChildren().remove(this.root);
        }
        this.root = root;
        this.getChildren().add(this.root);
        this.drag();
    }

    public void resetRoot() {

    }

    public void setOprIndex(int oprIndex) {
        this.oprIndex = oprIndex;
    }

    public void setSelectedNode(AbstractMapNode selectedNode) {
        this.selectedNode.set(selectedNode);
    }

    public void setOperations(ArrayList<Operation> operations) {
        this.operations = operations;
    }

    public void setRedoing(boolean redoing) {
        isRedoing = redoing;
    }

    public void setUndoing(boolean undoing) {
        isUndoing = undoing;
    }
}
